<?php
	abstract class MyDebuger {
		/* mostrar cada paso realizado */
		protected $debugStat	= False;
		/* mostrar la salida en formato HTML */
		protected $html		= False;
		/* tiempo de ejecucion */
		protected static $timeNow		= 0;
		protected static $timeStart	= 0;
		/* bandera para la demo de tiempo */
		protected $timeEnabled	= False;
		/* velocidad de la depuracion */
		protected $timeStep		= 500000;
		/* nombre de la instancia */
		protected $name			= False;
		/* ultimo error producido */
		protected $lastError	= False;
		/* archivo de log */
		protected $logFile	= Null;
		
		/**
			DEFAULT METHOD FORMTAT
			public function _defaultMethodFormat_() {
				try {
					try {
						// CODE HERE
					} catch (ErrorException $e) {
						// capturamos todos los errores y los transformamos en mi excepcion
						$this->convertToMyException($e);
					}
				} catch (MyException $e) {
					// si se produjo una excepcion la manejamos con el metodo 'show'
					$this->show($e->getMensaje($this->html));
				}
			}
		 **/
		
		public function __construct($nombre = False) {
			/* verificamos si existe la clase necesaria para el funcionamiento */
			$this->verificarClase();
			/* asignamos el el manejador de errores al metodo interno */
			set_error_handler(__CLASS__ . '::manejadorErrores', E_ALL);
			/* almacenamos el tiempo de inicio */
			$this->saveTime();
			/* asignamos el nombre al objeto */
			$this->name = $nombre;
		}
		
		public function __destruct() {
			/* restauramos el manejador de errores */
			restore_error_handler();
			/* cerramos el archivo log si existe */
			if ($this->logFile !== Null)
				fclose($this->logFile);
		}
		
		public static function manejadorErrores($numero, $mensaje, $archivo, $linea) {
			/* modificamos los errores a ecepciones */
			throw new ErrorException($mensaje, 0, $numero, $archivo, $linea);
		}
		
		public final function enableDebug($enable = True) {
			/* habilitamos la salida */
			$this->debugStat = $enable;
		}
		
		public final function enableHtmlDebug($enable = True) {
			/* habilitamos el formateo HTML */
			$this->html = $enable;
			/* habilitamos la salida de mensajes */
			$this->enableDebug();
		}
		
		public final function enableDemoTime($fast = False) {
			/* habilitamos la bandera de demo para el tiempo */
			$this->timeEnabled = True;
			/* verificamo si se desea mas rapido */
			if ($fast)
				/* aceleramos el tiempo */
				$this->timeStep = 100000;
		}
		
		public final function getError($vaciar = False, $interno = False) {
			try {
				/* almacenamos temporalmente el error */
				$errorReturn = ($this->lastError === False ? False : $this->lastError);
				/* verificamos si es para utilizacion interna */
				if ($interno)
					/* si es para utilizacion interna, retornamos aqui el error para no mostrar mensajes */
					return $errorReturn;
				/* mostramos un mensaje para identificar lo que estamos haciendo */
				$this->show('Enviando ultimo error..', False);
				/* si se especifico vaciar eliminamos el ultimo error */
				if ($vaciar)
					$this->setError(False);
				/* mostramos OK al mensaje */
				$this->showOK();
				/* retornamos el error */
				return $errorReturn;
			} catch (MyException $e) {
				$this->show($e->getMensaje($this->html));
			}
		}
		
		private function verificarClase() {
			/* vamos a verificar la clase MyException */
			$clase = 'MyException';
			/* verificamos si existe */
			if (!class_exists($clase))
				/* si no existe matamos el proceso con un mensaje */
				die('Se necesita de la clase \'' . $clase . '\' para utilizar la clase \'' . __CLASS__ . '\'');
		}
		
		protected final function enableLog($fileName = False) {
			try {
				/* verificamos que se asigno un nombre para el log */
				if ($fileName === False)
					$this->newException('No se especifico el nombre para el fichero log');
				/* habilitamos el log en un archivo */
				$this->logFile = fopen($fileName, 'a+');
			} catch (ErrorException $e) {
				$this->logFile = Null;
				$this->show('No se pudo crear el fichero log');
			} catch (MyException $e) {
				$this->show($e->getMensaje($this->html));
			}
		}
		
		protected final function log($msg = False) {
			try {
				/* verificamos que se ingreso el mensaje para el log */
				if ($msg === False)
					$this->newException('No se especifico el mensaje a guardar en el log');
				/* verificamos que exista un archivo abierto */
				if ($this->logFile === Null)
					$this->newException('No existe ningun fichero log abierto');
				/* guardamos el registro log */
				fwrite($this->logFile, '[' . $this->getName() . '::log ' . date('Y-m-d H:i:s') . '] ' . $msg . "\n");
			} catch (MyException $e) {
				$this->show($e->getMensaje($this->html));
			}
		}
		
		protected final function getName() {
			/* retornamos el nombre del objeto */
			return ($this->name ? $this->name : __CLASS__);
		}
		
		protected final function setError($error = False) {
			/* guardamos el error */
			$this->lastError = $error;
		}
		
		protected final function italic($cadena, $italic = True) {
			/* enviamos el mensaje con formato */
			return (
				/* verificamos si esta la bandera de HTML */
				$this->html ?
				/* si esta habilitada */
				(
					/* verificamos si se inicia o cierra el formato */
					$italic ?
					/* abrimos el formato */
					'<i>'.$cadena.'</i>' :
					/* cerramos el formato */
					'</i>'.$cadena.'<i>'
				) :
				/* sino, solo enviamos la cadena sin formato */
				$cadena
			);
		}
		
		protected final function saveTime() {
			/* guardamos el tiempo de inicio */
			self::$timeStart = $this->getTime();
		}
		
		protected final function getTimeNow() {
			/* agregamos el tiempo transcurrido */
			self::$timeNow = $this->getTime() - self::$timeStart;
			/* retornamos el tiempo con formato de 4 decimales */
			return str_pad(number_format(self::$timeNow, 3, ',', '.'), 7, '0', STR_PAD_LEFT);
		}
		
		protected final function getTime() {
			$timeInicio = explode(" ",microtime());
			/* retornamos el tiempo actual */
			return $timeInicio[1] + $timeInicio[0];
		}
		
		protected final function getDebugStatus() {
			/* retornamos el estado de la depuracion */
			return $this->debugStat;
		}
		
		protected final function newException($msg) {
			/* creamos una exepcion temporal para obtener el lugar de origen y agregarlo al error */
			$tempException = new MyException('error');
			/* asignamos el error con el lugar de origen (llamada al metodo) */
			$this->setError($msg . $tempException->getFrom($this->html));
			/* enviamos una exepcion para mostrar el error */
			throw new MyException($this->getError(False, True));
		}
		
		private function cleanHTML($cadena) {
			$cadena = str_replace(
				Array('<i>', '</i>', '<font', '</font>', '\'>'),
				Array('|i|', '|/i|', '|font', '|/font|', '\'|'),
				$cadena
			);
			$cadena = str_replace(
				Array('<', '>'),
				Array('&lt;', '&gt;'),
				$cadena
			);
			return str_replace(
				Array('|i|', '|/i|', '|font', '|/font|', '\'|'),
				Array('<i>', '</i>', '<font', '</font>', '\'>'),
				$cadena
			);
		}
		
		protected final function show($cadena, $salto = True) {
			/* si esta habilitada la bandera de tiempo */
			if ($this->timeEnabled)
				/* esperamos un tiempo aleatorio */
				usleep(rand(1000, $this->timeStep));
			/* armamos la cadena de salida */
			$cadena = (
				/* verificamos si se quiere con salto */
				$salto ?
				/* si se quiere con salto no agregamos el nombre */
				'' :
				/* si no se quiere con salto agregamos el nombre */
				'[' . $this->getTimeNow() . ' ' . $this->getName() . '::debug] '
			) . $this->cleanHTML($cadena);
			/* si esta habilitala la bandera de depuracion */
			if ($this->getDebugStatus())
				/* mostramos la salida */
				echo (
					$this->html ?
					/* si esta habilitada la bandera de formato HTML formateamos la salida */
					"<code>$cadena</code>" . (
						$salto ?
						/* si por parametros se habilito el salto de linea agregamos el salto */
						"<br/>\n" :
						/* si no se habilito el salto de linea agregamos un espacio */
						'&nbsp;'
					) :
					/* si no esta habilitada la bandera de formato HTML enviamos la salida sin formatear */
					$cadena . (
						$salto ?
						/* si por parametros se habilito el salto de linea agregamos el salto */
						"\n" :
						/* si no se habilito el salto de linea agregamos un espacio */
						' '
					)
				);
			/* vaciamos el buffer de salida */
			flush();
			/* si esta habilitada la bandera de tiempo */
			if ($this->timeEnabled)
				/* esperamos un tiempo aleatorio */
				usleep(rand(1000, round($this->timeStep / 2)));
		}
		
		protected function showOK() {
			/* mostramos la cadena */
			$this->show('OK');
		}
		
		protected function convertToMyException($e) {
			/* obtenemos el mensaje de error */
			$msje = $e->getMessage();
			/* obtenemos el fichero de origen */
			$file = basename($e->getFile());
			/* verificamos si la cadena de error ya tiene el nombre del fichero */
			if (!strstr($msje, $file))
				/* si no tiene agregamos el nombre del fichero y la linea de error */
				$msje	.= ', called in ' . $file . ':' . $e->getLine();
			/* enviamos la nueva excepcion */
			$this->newException($msje);
		}
	}
?>